package BlockR.function;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 *
 * @author zfh
 * @date 16-4-19
 * @ref http://blog.csdn.net/zhufenghao/article/details/51192043
 */
public class MappedBiggerFileReader {
    private MappedByteBuffer[] mappedBufArray;
    private int count = 0;
    private int number;
    private FileInputStream fileIn;
    private long fileLength;
    private int arraySize;
    public byte[] array;
    private static byte[][] All_Piece;
    private static byte[][][] All_Block;
    private static boolean add;
    private static int piece_index = 0;
    private static int block_index = 0;
    private static int piece_max=0;

    private MappedBiggerFileReader(String fileName, int arraySize) throws IOException {
        this.arraySize = arraySize;
        piece_max=arraySize*8;
        piece_index = 0;
        block_index = 0;
        add = false;
        All_Piece = null;
        All_Block = null;
        this.fileIn = new FileInputStream(fileName);
        FileChannel fileChannel = fileIn.getChannel();
        this.fileLength = fileChannel.size();
        this.number = (int) Math.ceil((double) fileLength / (double) Integer.MAX_VALUE);
        long piece_num = GetPieceNum(arraySize);
        int block_num = GetBlockNum(piece_num);
        All_Piece = new byte[(int) piece_num][arraySize];
        All_Block = new byte[block_num][piece_max][arraySize];
        this.mappedBufArray = new MappedByteBuffer[number];// 内存文件映射数组
        long preLength = 0;
        long regionSize = (long) Integer.MAX_VALUE;// 映射区域的大小
        for (int i = 0; i < number; i++) {// 将文件的连续区域映射到内存文件映射数组中
            if (fileLength - preLength < (long) Integer.MAX_VALUE) {
                regionSize = fileLength - preLength;// 最后一片区域的大小
            }
            mappedBufArray[i] = fileChannel.map(FileChannel.MapMode.READ_ONLY, preLength, regionSize);
            preLength += regionSize;// 下一片区域的开始
        }

    }

    private int GetBlockNum(long piece_num) {
        int num_block;
        if (piece_num % piece_max == 0) {
            num_block = (int) (piece_num / piece_max);
        } else {
            num_block = (int) (piece_num / piece_max + 1);
        }
        return num_block;
    }

    private int read(){
        if (count >= number) {
            return -1;
        }
        int limit = mappedBufArray[count].limit();
        int position = mappedBufArray[count].position();
        if (limit - position > arraySize) {
            array = new byte[arraySize];
            mappedBufArray[count].get(array);
            All_Piece[piece_index] = array;
            All_Block[block_index][piece_index % piece_max] = array;
            piece_index++;
            if (piece_index % piece_max == 0) {
                block_index++;
            }
            return arraySize;
        } else {// 本内存文件映射最后一次读取数据
            array = new byte[limit - position];
            mappedBufArray[count].get(array);
            All_Piece[piece_index] = array;
            All_Block[block_index][piece_index % piece_max] = array;
            piece_index++;
            if (piece_index % piece_max == 0) {
                block_index++;
            }
            if (count < number) {
                count++;// 转换到下一个内存文件映射
            }
            return limit - position;
        }
    }

    private void close() throws IOException {
        fileIn.close();
        array = null;
    }

    public static void Read_File(String fileName, int arraySize) throws IOException {
        long start_time = System.currentTimeMillis();
        MappedBiggerFileReader reader = new MappedBiggerFileReader(fileName, arraySize);
        if (reader.fileLength == 0) {
            return;
        }
        while (reader.read() != -1) {}
        reader.close();
        long end_time = System.currentTimeMillis();
        System.out.println("读取文件+分片+分块耗时：" + String.valueOf(end_time - start_time));
    }

    public static boolean isAdd() {
        return add;
    }

    private long GetPieceNum(int array_size) {
        long piece_num;
        if (this.fileLength % array_size == 0) {
            piece_num = fileLength / array_size;
        } else {
            piece_num = fileLength / array_size + 1;
        }
        long final_piece_length = fileLength % array_size;
        if (final_piece_length == 0) {
            final_piece_length = array_size;
        }
        if (final_piece_length <= array_size-3) { //[0,1000]  不需要添加新片  3是要填充的字节的长度
            add = false;
        } else if (final_piece_length <= array_size) {  //(1000.piece_max] 需要添加新片
            piece_num++;
            add = true;
        }
        return piece_num;
    }

    public static byte[][] getAll_Piece() {
        return All_Piece;
    }

    public static byte[][][] getAll_Block() {
        return All_Block;
    }

    public static int getBlock_index() {
        return block_index;
    }

    public static int getPiece_index() {
        return piece_index;
    }

    public static void main(String[] arg) throws IOException {
/*        long start_time = System.currentTimeMillis();
        String filename = "D:\\test_doc\\test1.txt";
        int array_size = 128;
        MappedBiggerFileReader reader = new MappedBiggerFileReader(filename, array_size);
        while (reader.read() != -1) ;
        reader.close();
        long end_time = System.currentTimeMillis();
        System.out.println("读取文件+分片+分块耗时：" + String.valueOf(end_time - start_time));*/
/*        StringBuilder stringBuilder=new StringBuilder();
        for(int file_num=0;file_num<All_Piece.length;file_num++){
            stringBuilder.append(new String(All_Piece[file_num],"UTF-8"));
        }
        System.out.println(stringBuilder.toString());*/
        byte[] final_piece = All_Piece[piece_index - 2];
        byte[] end_piece = All_Piece[piece_index - 1];
        byte[] union = new byte[final_piece.length + end_piece.length];
        System.arraycopy(final_piece, 0, union, 0, final_piece.length);
        System.arraycopy(end_piece, 0, union, final_piece.length, end_piece.length);
        System.out.println(new String(union, "UTF-8"));
    }
}